<template>
  <view class="load-refresh">
    <!-- 刷新动画，可自定义，占高100rpx -->
    <view class="animation" :style="{'--color': color}">
      <view v-if="pullDown && state !== 2" class="remind"> {{moving ? '松开立即刷新' : '继续下拉刷新'}} </view>
      <view v-if="pullDown && state === 2" class="refresh">
        <xl-loading :type="loadingType" :size="30" :color="color"></xl-loading>
      </view>
    </view>
    <!-- 数据列表块 -->
    <view
        class="cover-container"
        :style="[{
				background: background,
				transform: coverTransform,
				transition: coverTransition
			}]"
        @touchstart="coverTouchstart"
        @touchmove="coverTouchmove"
        @touchend="coverTouchend">
      <view v-if="!fixed" class="list" :style="{padding: $xl.parseUnit(padding, 'rpx')}">
        <!-- 数据集插槽 -->
        <slot name="list" :items="dataList"></slot>
        <view style="height: 40upx;"></view>
        <slot v-if="state === 4" name="empty">
          <xl-empty :text="emptyMsg" color="#888" :top="emptyTop"></xl-empty>
        </slot>
        <!-- 提示信息 -->
        <xl-divider-tip v-else-if="state !== 3 || (state === 3 && showNoMore)" :loading="state === 1" width="80%" :fontSize="14" textColor="#aaa" :lineColor="['#fff', '#aaa']" :backgroundColor="background">{{stateText}}</xl-divider-tip>
      </view>
      <scroll-view v-else scroll-y class="list" :scroll-top="scrollTop" @scrolltolower="loadMore" :style="[scrollHeight, {padding: $xl.parseUnit(padding, 'rpx')}]">
        <!-- 数据集插槽 -->
        <slot name="list" :items="dataList"></slot>
        <view style="height: 40upx;"></view>
        <slot v-if="state === 4" name="empty">
          <xl-empty :text="emptyMsg" color="#888" :top="emptyTop"></xl-empty>
        </slot>
        <!-- 提示信息 -->
        <xl-divider-tip v-else-if="state !== 3 || (state === 3 && showNoMore)" :loading="state === 1" width="80%" :fontSize="14" textColor="#aaa" :lineColor="['#fff', '#aaa']" :backgroundColor="background">{{stateText}}</xl-divider-tip>
      </scroll-view>
    </view>
  </view>
</template>

<script setup>
import { ref, computed, onMounted, watch } from 'vue'
import { onReachBottom  } from "@dcloudio/uni-app";

// 组件传参
import propsData from './props.js'
const props = defineProps(propsData)
// 组件触发方法
const emits = defineEmits(['provider', 'initLoad', 'reload', 'reachBottom'])
// 提供给父组件进行ref来切换
defineExpose({ initLoad, reload, loadMore, push, getData, addData, updateData, deleteData })

// 计算组件高度
const scrollHeight = computed(()=>{
  if(props.fixed){
    if (Number(props.fixedHeight)) {
      let height = uni.$xl.parseUnit(props.fixedHeight, 'rpx');
      return `height: ${height};`
    } else {
      let height = uni.getWindowInfo().screenHeight - uni.getWindowInfo().statusBarHeight - uni.$xl.upx2px(0 + props.reduceHeight)
      return `height: ${height}px; -webkit-overflow-scrolling:touch;`
    }
  }else{
    return "";
  }
})

// 触发父组件进行加载数据
function provide(){
  emits('provider', props.cursor ? {
    cursor: cursor,
    pageSize: props.pageSize
  } : {
    pageNum: currentPage,
    pageSize: props.pageSize
  }, push)
}

let currentPage = 1  // 当前页
let cursor = ''     // 当前游标
const dataList = ref([]) // 数据列表
const state = ref(0)  // 状态：0、上拉加载更多，1、加载中，2、刷新中，3、暂时没有数据，4、已经到底啦~
// 判断loadText，可以根据需求自定义
const stateText = computed(()=>{
  var text = '';
  switch(state.value){
    case 0:text = '上拉加载更多';break;
    case 1:text = '加载中';break;
    case 2:text = '更新中';break;
    case 2.5:text = '更新成功';break;
    case 3:text = '已经到底啦';break;
    case 4:text = '暂时没有数据';break;
    case 5:text = '发生错误，请重试';break;
    default:text = '没有更多内容';break;
  }
  return text;
})

// 初始化的时候，会执行一次加载
onMounted(() => {
  if(!props.lazyLoad){
    // 加载中
    state.value = 1;
    provide();
  }
})

let firstLoad = true    // 是否第一次加载，每次切换的时候来判断是否第一次，是则加载，不是则不加载
watch(() => props.load, (newValue, oldValue) => {
  if(newValue) initLoad()
})
// 若为懒加载，不使用init字段控制的话，则可以使用这个方法来初始化加载数据，第二次使用时，会判断之前是否已经初始化过了
function initLoad(){
  if(firstLoad){
    firstLoad = false;
    reload();
    emits("initLoad");
  }else if(props.refresh){
    // 如果再显示时，可以刷新，则刷新数据
    reload();
    emits("reload");
  }
}
// 重新加载数据
function reload(){
  if(state.value === 1 || state.value === 2) return;
  state.value = 1;

  dataList.value = []
  currentPage = 1
  provide();
}
// scroll-view上划会触发加载更多数据loadMore
function loadMore() {
  // 只有在可上划加载的状态才能执行加载
  if(state.value !== 0 ) return;
  // 请求下一页
  currentPage++;
  // 加载中
  state.value = 1;
  provide();
  emits("reachBottom");
}
// 不是固定窗口，用页面触底方法onReachBottom
if(!props.fixed){
  onReachBottom(() => {
    loadMore()
  })
}

let moving = ref(false)
let scrollTop = ref(-1)
let coverTransform = ref('translateY(0px)')
let coverTransition = ref('0s')
// 更新数据
function push(arr){
  // 如果是下拉刷新，需要先动画处理，再添加数据
  if(state.value === 2){
    moving.value = false
    scrollTop.value = -1
    coverTransform.value = 'translateY(0px)';
    coverTransition.value = 'transform 0.3s linear';
    state.value = 2.5;
    setTimeout(() => {
      // 清空原来的数据
      dataList.value = []
      // 刷新数据
      pushData(arr);
    }, 500)
  }else{
    // 如果不是下拉刷新，则直接添加
    pushData(arr);
  }
}
// 向列表中添加数据，并更新状态
function pushData(arr){
  // 有数据push了，不管是否为空，都表示已经加载过一次了
  firstLoad = false;
  if(arr && arr.length>=0){
    // 有数据
    state.value = 0;
  }else{
    // 出现错误
    state.value = 5;
    return;
  }

  if(arr.length < props.pageSize){
    // 数据没有一页的数量，说明到底了
    state.value = 3;
  }

  dataList.value.push(...arr)
  if(dataList.value.length === 0){
    // 如果dataList里也一个数据都没有，表示没有数据
    state.value = 4;
  }
  if(props.cursor) {
    cursor = arr.length ? arr[arr.length - 1][props.cursorProp || 'id'] : ''
  }
}

// 获取数据列表
function getData(index) {
  if(index === undefined) return dataList.value;
  else return dataList.value[index];
}
// 动态向列表添加数据，head表示头部添加，不更新状态，比如评论向列表添加一条数据
function addData(arr, head=true){
  if(head){
    dataList.value.unshift(...arr);
  }else{
    dataList.value.push(...arr)
  }
  if(state.value === 4) state.value = 3;
}
// 动态删除列表中一个数据，例如删除一条评论
function updateData(index, data){
  dataList.value.splice(index, 1, data);
  if(state.value === 4) state.value = 3;
}
// 动态删除列表中一个数据，例如删除一条评论
function deleteData(index){
  dataList.value.splice(index, 1);
  if(dataList.value.length === 0) {
    state.value = 4
  }
}

let startY = 0
let moveY = 0
// 回弹效果
function coverTouchstart(e) {
  if (!props.pullDown) {
    return
  }
  coverTransition.value = 'transform .1s linear'
  startY = e.touches[0].clientY
}
function coverTouchmove(e) {
  // 取消事件的默认动作，通知 Web 浏览器不要执行与事件关联的默认动作，不加浏览器下拉会触发整个页面下拉
  if(props.fixed) e.preventDefault();
  // 关闭下拉，以及加载中、更新中时不执行
  if (!props.pullDown || state.value === 1 || state.value === 2) {
    return
  }
  moveY = e.touches[0].clientY
  let moveDistance = moveY - startY
  // 上拉不触发
  if (moveDistance < 0) return
  // 下拉
  if (moveDistance <= 50) {
    coverTransform.value = `translateY(${moveDistance}px)`
  }
  moving.value = moveDistance >= 50
}
function coverTouchend() {
  if (!props.pullDown || state.value === 1 || state.value === 2) {
    return
  }
  if (moving.value) {
    runRefresh()
  } else {
    coverTransition.value = 'transform 0.3s cubic-bezier(.21,1.93,.53,.64)'
    coverTransform.value = 'translateY(0px)'
  }
}
// 代码内触发下拉刷新
function runRefresh() {
  // 更新中
  state.value = 2;
  scrollTop.value = 0
  coverTransition.value = 'transform .1s linear'
  coverTransform.value = 'translateY(50px)'
  cursor = ''
  currentPage = 1;
  provide();
}

</script>

<style lang="scss" scoped>
$color: var(--color);

.load-refresh{
  margin: 0;
  padding: 0;
  width: 100%;
  .cover-container{
    width: 100%;
    margin-top: -100rpx;
    .list{
      width: 100%;
      height: 100%;
      min-height: 200upx;
      box-sizing: border-box;
      &::before{
        content: '';
        display: table;
      }
      .load-more{
        font-size: 24rpx;
        text-align: center;
        color: #AAAAAA;
        padding: 16rpx;
      }
    }
  }
}

/* 动画 */
.animation {
  width: 100%;
  height: 100rpx;
  color: $color;
  .remind {
    width: 100%;
    height: 100rpx;
    text-align: center;
    line-height: 100rpx;
    font-size: 28rpx;
    color: $color;
  }
  .refresh {
    width: 100%;
    height: 100rpx;
    display: flex;
    align-items: center;
    justify-content: center;
    box-sizing: border-box;
    view {
      // animation-play-state: $playState!important;
    }
  }
}
</style>
